// 每个区县可伸缩块
import {
	Viewer,
	Color,
	Primitive,
	PolygonGeometry,
	PolylineGeometry,
	WallGeometry,
	PolygonHierarchy,
	GeometryInstance,
	Material,
	MaterialAppearance,
	PolylineMaterialAppearance,
	DebugAppearance,
	Cartesian3,
	VertexFormat,
	Matrix4,
	PrimitiveCollection,
	ScreenSpaceEventHandler,
	ScreenSpaceEventType,
	defined,
	RectangleGeometry,
	Rectangle,
	Math as CSMath,
	CylinderGeometry
} from 'cesium'
class DistrictBlock {

	viewer : Viewer | null
	isLoad : boolean = false
	
	extrudeHeight: number = 25000

	districkPrimitives : PrimitiveCollection | null
	eventHandler : ScreenSpaceEventHandler | null

	contentColor : Array<any> = [102, 184, 217, '0.2']
	contentHColor : Array<any> = [102, 184, 217, '1.0']
	wallColor : Array<any> = [102, 184, 217, 0.1]
	outlineColor : Array<any> = [168, 239, 255, 1.0]
	labelColor: Array<any> = [255,255,255,255]
	labelDimandColor: Array<any> = [255,255,255,255]

	constructor(viewer : Viewer) {
		this.viewer = viewer
	}

	// 加载数据
	async load(code : string) {
		if (this.isLoad) return
		this.isLoad = true

		const result = await import(`../../../../static/gisData/geojson/${code}Q.json`)
		if (result == null) return

		const blocks = this.parseGeojson(result)

		this.districkPrimitives = new PrimitiveCollection()
		this.viewer.scene.primitives.add(this.districkPrimitives)

		this.bindEventHandler(this.viewer)

		blocks.map((blockLonlats : Array<Array<number>>) => {
			this.createContentPrimitive(blockLonlats)
			this.createOutlinePrimitive(blockLonlats)
			this.createWallPrimitive(blockLonlats)
		})
		result && result.features.map((feature: any)=>{
			this.createLabelPrimitive(feature)
		}) 
	}

	// 将geojson解析成坐标数组
	private parseGeojson(geojson : any) {
		const extrudeHeight = this.extrudeHeight
		if (geojson == null) return []
		if (geojson.type == 'FeatureCollection') {
			return parseFeatureC(geojson)
		}
		else if (geojson.type == 'Feature') {
			return parseFeacture(geojson)
		}

		function parseFeatureC(geojson : any) {
			if (geojson.type == 'FeatureCollection') {
				return geojson.features.map((item : any) => { return parseFeacture(item) })
			}
			else return null
		}

		function parseFeacture(geojson : any) {
			if (geojson.type != 'Feature') return null
			if (geojson.geometry == null) return null
			const geometry = geojson.geometry
			return parseGeometry(geometry)
		}

		function parseGeometry(geometry : any) {
			if (geometry.type == 'MultiPolygon') {
				const coordinates = geometry.coordinates
				return coordinates.map((coors : any) => {
					let lonlats = []
					coors[0].forEach((item : any) => { lonlats = lonlats.concat([...item, extrudeHeight]) })
					return lonlats
				})

			}
			else if (geometry.type == 'Polygon') {
				const coordinates = geometry.coordinates[0]
				let lonlats = []
				coordinates.forEach((item : any) => { lonlats = lonlats.concat([...item, extrudeHeight]) })
				return [lonlats]
			}
			else return null
		}
	}

	// 创建Content几何
	private createContentPrimitive(lonlatsArray : Array<Array<any>>) {
		if (lonlatsArray == null || !(lonlatsArray instanceof Array) || lonlatsArray.length == 0) return

		const color = this.contentColor
		const extrudeHeight = this.extrudeHeight

		const instances = lonlatsArray.map((lonlats : Array<any>) => {
			const block = new PolygonGeometry({
				polygonHierarchy: new PolygonHierarchy(Cartesian3.fromDegreesArrayHeights(lonlats)),
				vertexFormat: VertexFormat.POSITION_NORMAL_AND_ST,
				extrudedHeight: extrudeHeight
			})

			return new GeometryInstance({
				geometry: block,
				id: 'districtBlock-instance',
				modelMatrix: Matrix4.IDENTITY.clone(),
			})
		})

		const contentAppearance = new MaterialAppearance({
			translucent: true,
			closed: true,
			vertexShaderSource: `
				in vec3 position3DHigh;
				in vec3 position3DLow;
				in vec3 normal;
				in vec2 st;
				in float batchId;
				uniform float grow;
				
				out vec3 v_positionEC;
				out vec3 v_positionMC;
				out vec3 v_normalEC;
				out vec2 v_st;
				out float v_grow;
				
				void main()
				{
				    vec4 p = czm_computePosition();
				
				    v_positionEC = (czm_modelViewRelativeToEye * p).xyz;      // position in eye coordinates
				    v_positionMC = (czm_inverseModel * vec4(p.xyz, 1.0)).xyz;
				    v_normalEC = czm_normal * normal;                         // normal in eye coordinates
				    v_st = st;
                    v_positionMC.z = v_positionMC.z - (1.0-grow)*${extrudeHeight}.0;
					v_grow = grow;
				    gl_Position = czm_modelViewProjectionRelativeToEye * ( czm_model * vec4(v_positionMC,1.0));
				}
			`,
			fragmentShaderSource: `
				in vec3 v_positionEC;
				in vec3 v_positionMC;
				in vec3 v_normalEC;
				in vec2 v_st;
				in float v_grow;
				
				void main()
				{
				    czm_materialInput materialInput;
				    czm_material material = czm_getMaterial(materialInput);
				    out_FragColor = vec4(material.diffuse + material.emission, material.alpha * v_grow);        
				}
			`,
			material: new Material({
				fabric: {
					type: 'Color',
					uniforms: {
						color: new Color(color[0] / 255, color[1] / 255, color[2] / 255, color[3]),
						
					},
					
				},

			})

		})
        contentAppearance.uniforms = {
			grow: 0
		}

		const contentPrimitive = new Primitive({
			geometryInstances: instances,
			appearance: contentAppearance,
			asynchronous: true,
			releaseGeometryInstances: true,
			compressVertices: true
		})

		this.districkPrimitives.add(contentPrimitive)
		
		this.countdownFrameRender(1, (rate: number) => {
			contentAppearance.uniforms.grow = rate
		})
	}

	// 创建Outline几何
	private createOutlinePrimitive(lonlatsArray : Array<Array<any>>) {
		if (lonlatsArray == null || !(lonlatsArray instanceof Array) || lonlatsArray.length == 0) return

		const color = this.outlineColor
		const extrudeHeight = this.extrudeHeight

		const instances = lonlatsArray.map((lonlats : Array<any>) => {
			const outlineGeom = new PolylineGeometry({
				positions: Cartesian3.fromDegreesArrayHeights(lonlats),
			})

			return new GeometryInstance({
				geometry: outlineGeom,
				id: 'outlineBlock-instance',
				modelMatrix: Matrix4.IDENTITY.clone(),
			})

		})

		const outlineAppearance = new PolylineMaterialAppearance({
			material: new Material({
				fabric: {
					uniforms: {
						color: new Color(color[0] / 255, color[1] / 255, color[2] / 255, color[3]),
						grow: 0
					},
					source: `
					uniform vec4 color;
					uniform float grow;
					
					czm_material czm_getMaterial(czm_materialInput materialInput) {
						czm_material material = czm_getDefaultMaterial(materialInput);
						vec2 st = materialInput.st;
						material.diffuse = color.rgb;
						material.alpha = color.a * grow;
						
						return material;
					}
					`
				}
			})
		})
		
		outlineAppearance.uniforms = {
			grow: 0
		}

		const outlinePrimitive = new Primitive({
			geometryInstances: instances,
			appearance: outlineAppearance,
			asynchronous: true,
			releaseGeometryInstances: true,
			compressVertices: true,
			allowPicking: false
		})

		this.districkPrimitives.add(outlinePrimitive)
		
		this.countdownFrameRender(1, (rate: number)=>{
			outlineAppearance.material.uniforms.grow = rate
		})
	}

	// 创建Wall几何
	private createWallPrimitive(lonlatsArray : Array<Array<any>>) {
		if (lonlatsArray == null || !(lonlatsArray instanceof Array) || lonlatsArray.length == 0) return

		const color = this.wallColor

		const instances = lonlatsArray.map((lonlats : Array<any>) => {
			const outlineGeom = new WallGeometry({
				positions: Cartesian3.fromDegreesArrayHeights(lonlats),
			})

			return new GeometryInstance({
				geometry: outlineGeom,
				id: 'outlineBlock-instance',
				modelMatrix: Matrix4.IDENTITY.clone(),
			})

		})

		const outlineAppearance = new MaterialAppearance({
			material: new Material({
				fabric: {
					uniforms: {
						color: new Color(color[0] / 255, color[1] / 255, color[2] / 255, color[3]),
						grow: 0
					},
					source: `
						uniform vec4 color;
						czm_material czm_getMaterial(czm_materialInput materialInput) {
							czm_material material = czm_getDefaultMaterial(materialInput);
							vec2 st = materialInput.st;
							material.diffuse = color.rgb;
							material.alpha = color.a;
							if(st.t > grow) material.alpha = 0.0;
							return material;
						}
					`
				}
			})
		})

		const outlinePrimitive = new Primitive({
			geometryInstances: instances,
			appearance: outlineAppearance,
			asynchronous: true,
			releaseGeometryInstances: true,
			compressVertices: true,
			allowPicking: false
		})

		this.districkPrimitives.add(outlinePrimitive)
		
		this.countdownFrameRender(1, (rate: number)=>{
			outlineAppearance.material.uniforms.grow = rate
		})
	}

    // 创建市名标签 
	private createLabelPrimitive(feature: any) {
		const labelColor = this.labelColor
		const labelDiamandColor = this.labelDimandColor
		
		if(feature == null) return
		const name = feature.properties.name
		const lonlat = feature.properties.center
		
		const labelInstances = new GeometryInstance({
			id: 'label_name_instance',
			geometry: new RectangleGeometry({
				height: this.extrudeHeight + 5000,
				rectangle: new Rectangle(
				CSMath.toRadians(lonlat[0] - 0.5),
				CSMath.toRadians(lonlat[1] - 0.2),
				CSMath.toRadians(lonlat[0] + 0.5),
				CSMath.toRadians(lonlat[1] + 0.2))
			})
		})
		const labelAppearance = new MaterialAppearance({
			vertexShaderSource: `
			in vec3 position3DHigh;
			in vec3 position3DLow;
			in vec3 normal;
			in vec2 st;
			in float batchId;
			
			out vec3 v_positionEC;
			out vec3 v_normalEC;
			out vec2 v_st;
			out vec3 v_positionMC;
			
			void main()
			{
			    vec4 p = czm_computePosition();
			
			    v_positionEC = (czm_modelViewRelativeToEye * p).xyz;      // position in eye coordinates
			    v_normalEC = czm_normal * normal;                         // normal in eye coordinates
			    v_st = st;
				v_positionMC = (czm_inverseModel * vec4(p.xyz, 1.0)).xyz;
				float radian = radians(20.0);
				float cos = cos(radian);
				float sin = sin(radian);
			    mat3 rotateMatrix4 = mat3(cos,0,-sin,  0,1,0,  sin,0,cos);
				vec3 cameraPositionMC = czm_encodedCameraPositionMCHigh + czm_encodedCameraPositionMCLow;
				vec3 originMC = p.xyz + cameraPositionMC;
				//originMC = rotateMatrix4 * originMC;
				//originMC.x += mod(czm_frameNumber/1000.0, 2.0) *  100000.0;
				originMC.y += mod(czm_frameNumber/1000.0, 2.0) *  100000.0;
				//originMC.z += mod(czm_frameNumber/1000.0, 2.0) *  100000.0;
				originMC = originMC - cameraPositionMC;
				p.xyz = originMC;
				v_positionMC = originMC;
			    gl_Position = czm_modelViewProjectionRelativeToEye * p;
			}
			`,
			fragmentShaderSource: `
				in vec3 v_positionEC;
				in vec3 v_positionMC;
				in vec3 v_normalEC;
				in vec2 v_st;
				
				void main()
				{
				    czm_materialInput materialInput;
				    czm_material material = czm_getMaterial(materialInput);
					// material.diffuse.xyz = vec3(v_positionMC.y/255.0,0.0,0.0);
				    out_FragColor = vec4(material.diffuse, material.alpha);        
				}
			`,
			material: new Material({
				fabric: {
					type: 'Color'
				}
			})
		})
		
		const labelPrimitive = new Primitive({
			geometryInstances: labelInstances,
			appearance:labelAppearance,
			// asynchronous: true,
			// releaseGeometryInstances: true,
			// compressVertices: true
		})
		// this.districkPrimitives.add(labelPrimitive)
		
		// const diamandInstance = new GeometryInstance({
		// 	geometry: new CylinderGeometry({})
		// })
	}

	// 绑定事件
	private bindEventHandler(viewer : Viewer) {
		var highlightPrimitive = null
		if (this.eventHandler != null) this.unbindEventHandler()
		this.eventHandler = new ScreenSpaceEventHandler()
		this.eventHandler.setInputAction((event : any) => {
			const pickedObject = viewer.scene.pick(event.position)
			if (defined(pickedObject) && pickedObject.id == 'districtBlock-instance') {
				if (highlightPrimitive != null && highlightPrimitive != pickedObject.primitive) {
					highlightPrimitive.appearance.material.uniforms.color = new Color(
						this.contentColor[0] / 255,
						this.contentColor[1] / 255,
						this.contentColor[2] / 255,
						this.contentColor[3],
					)
				}
				highlightPrimitive = pickedObject.primitive
				highlightPrimitive.appearance.material.uniforms.color = new Color(
					this.contentHColor[0] / 255,
					this.contentHColor[1] / 255,
					this.contentHColor[2] / 255,
					this.contentHColor[3],
				)
			}
		}, ScreenSpaceEventType.LEFT_CLICK)
	}

	// 解绑事件
	private unbindEventHandler() {
		if (this.eventHandler == null) return
		this.eventHandler.removeInputAction(ScreenSpaceEventType.LEFT_CLICK)
		this.eventHandler.destroy()
		this.eventHandler = null
	}
	
	/**
	 * 倒计时帧动画
	 * @param {number} time 倒计时时间
	 * @param {Function} callback 渲染帧回调
	 */
	private countdownFrameRender(time : number, callback : CountdownFrameCallback) {
		const viewer = this.viewer
		return new Promise((resolve) => {
			const originalDate = Date.now()
			const frameFunc = () => {
				const dateNow = Date.now()
				const spend = dateNow - originalDate
				if (spend > time * 1000) {
					viewer.scene.preRender.removeEventListener(frameFunc)
					resolve('')
				}
				let rate = spend / (time * 1000)
				if (rate > 1) rate = 1
				callback(rate)
			}
			viewer.scene.preRender.addEventListener(frameFunc)
		})
	}

	// 卸载数据
	async unload() {
		if (this.isLoad == false) return
		this.isLoad = false
		
		await this.countdownFrameRender(0.5, function (rate : number) {
			if(this.districkPrimitives) {
				for(let i =0; i<this.districkPrimitives.length; i++) {
					const primitive = this.districkPrimitives.get(i)
					const uniforms = primitive.appearance.uniforms
					uniforms && (uniforms.grow = 1 - rate)
					const material = primitive.appearance.material
					material && material.uniforms && (material.uniforms.grow = 1 - rate)
				}
			}
		}.bind(this))
		
		this.unbindEventHandler()
		this.viewer.scene.primitives.remove(this.districkPrimitives)
		//this.districkPrimitives.destroy()
		this.districkPrimitives = null
		this.unbindEventHandler()
	}
}

export default DistrictBlock

/**
 * 倒计时帧回调
 */
type CountdownFrameCallback = (rate : number) => void